package com.toubutu.audio.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.toubutu.audio.admin.dto.AdminVideoQueryDto;
import com.toubutu.audio.admin.dto.MergeFileDto;
import com.toubutu.audio.admin.dto.UploadFileDto;
import com.toubutu.audio.admin.dto.VideoDto;
import com.toubutu.audio.dto.CheckFileExistDto;
import com.toubutu.audio.dto.PageQueryVideoDto;
import com.toubutu.audio.entity.UploadFileRecord;
import com.toubutu.audio.service.CollectionService;
import com.toubutu.audio.vo.SeqVideoItem;
import com.toubutu.audio.vo.VideoListItemVo;
import com.toubutu.audio.vo.VideoVo;
import com.toubutu.audio.common.BusinessConstants;
import com.toubutu.audio.entity.Video;
import com.toubutu.audio.entity.VideoCategory;
import com.toubutu.audio.mapper.VideoMapper;
import com.toubutu.audio.service.UploadFileRecordService;
import com.toubutu.audio.service.VideoCategoryService;
import com.toubutu.audio.service.VideoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.toubutu.common.BusinessException;
import com.toubutu.common.CurrentUserContext;
import com.toubutu.common.PageUtils;
import com.toubutu.common.StatusCodeEnum;
import com.toubutu.constants.BaseConstants;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 戒躁
 * @since 2024-01-08
 */
@Service
public class VideoServiceImpl extends ServiceImpl<VideoMapper, Video> implements VideoService {


    @Resource
    private UploadFileRecordService uploadFileService;
    @Resource
    private VideoCategoryService categoryService;
    @Resource
    private VideoMapper videoMapper;
    @Resource
    private CollectionService collectionService;

    @Override
    public PageUtils<VideoVo> pageQueryAdminVoList(AdminVideoQueryDto dto) {
        LambdaQueryWrapper<Video> wrapper = dto.getWrapper();
        wrapper.eq(Video::getRootId, BaseConstants.PARENT_MENU_ID);
        Page<Video> page = dto.getPage();
        page = page(page, wrapper);
        List<Video> records = page.getRecords();
        if (records.isEmpty()) {
            return new PageUtils<>(Collections.emptyList(), page);
        }
        Map<Integer, VideoCategory> videoCategoryMapInfo = getVideoCategoryMapInfo(records);
        //获取根节点id
        Set<Integer> rootIds = records.stream().filter(video -> video.getIsMultiple() != null && video.getIsMultiple().equals(BaseConstants.YES) && video.getRootId().equals(BaseConstants.PARENT_MENU_ID))
                .map(Video::getVideoId).collect(Collectors.toSet());
        Map<Integer, List<VideoVo>> childrenVideoMap = getChildrenVideoMap(rootIds);

        List<VideoVo> result = records.stream().map(video -> {
            VideoVo videoVo = buildAdminVideoVo(video, videoCategoryMapInfo);
            if (childrenVideoMap != null) {
                videoVo.setChildren(childrenVideoMap.get(video.getVideoId()));
            }
            return videoVo;
        }).collect(Collectors.toList());
        return new PageUtils<>(result, page);
    }


    private Map<Integer, List<VideoVo>> getChildrenVideoMap(Collection<Integer> rootIds) {
        if (rootIds.isEmpty()) {
            return null;
        }
        LambdaQueryWrapper<Video> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Video::getIsDelete, BaseConstants.NO).in(Video::getRootId, rootIds);
        List<Video> videoList = list(queryWrapper);
        if (videoList.isEmpty()) {
            return null;
        }
        Map<Integer, VideoCategory> videoCategoryMapInfo = getVideoCategoryMapInfo(videoList);
        return videoList.stream().map(video -> buildAdminVideoVo(video, videoCategoryMapInfo))
                .sorted(Comparator.comparingInt(VideoVo::getSeq)).collect(Collectors.groupingBy(VideoVo::getRootId));
    }

    private Map<Integer, VideoCategory> getVideoCategoryMapInfo(List<Video> videos) {
        List<Integer> categoryIds = videos.stream().map(Video::getCategoryId).collect(Collectors.toList());
        if (categoryIds.isEmpty()) {
            return null;
        }
        return categoryService.getCategoryMapInfo(categoryIds);
    }

    /**
     * 构建videoVo 对象
     *
     * @param video video PO
     * @return video VO
     */
    private VideoVo buildAdminVideoVo(Video video, Map<Integer, VideoCategory> categoryMap) {
        VideoVo videoVo = new VideoVo();
        BeanUtils.copyProperties(video, videoVo);
        if (StringUtils.isNotBlank(video.getActor())) {
            videoVo.setActor(Arrays.asList(video.getActor().split(BusinessConstants.SPLIT)));
        }
        if (StringUtils.isNotBlank(video.getDirector())) {
            videoVo.setDirector(Arrays.asList(video.getDirector().split(BusinessConstants.SPLIT)));
        }
        VideoCategory videoCategory = categoryMap.get(video.getCategoryId());
        if (videoCategory != null) {
            VideoCategory parentCategory = categoryMap.get(videoCategory.getParentId());
            if (parentCategory != null) {
                videoVo.setParentCategory(parentCategory);
            }
            videoVo.setCategory(videoCategory);
        }
        if (StringUtils.isNotBlank(video.getTag())) {
            videoVo.setTagList(Arrays.asList(video.getTag().split(BusinessConstants.SPLIT)));
        }
        videoVo.setIsMultiple(!(video.getIsMultiple() == null || video.getIsMultiple() == BaseConstants.NO));
        if (videoVo.getIsMultiple()) {
            videoVo.setChildren(Collections.emptyList());
        }
        String userId = CurrentUserContext.getUserId();
        videoVo.setIsCollected(Boolean.FALSE);
        if (userId != null) {
            Long loginUserId = Long.parseLong(userId);
            Boolean collect = collectionService.isCollect(loginUserId, videoVo.getVideoCode());
            videoVo.setIsCollected(collect);
        }
        return videoVo;
    }

    @Override
    public String uploadVideo(UploadFileDto uploadFileDto) throws IOException {
        return uploadFileService.uploadFileBySlice(uploadFileDto);
    }

    @Override
    public VideoVo getVideoInfoByCode(String code) {
        LambdaQueryWrapper<Video> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Video::getVideoCode, code).eq(Video::getIsDelete, BaseConstants.UN_DELETED);
        Video video = getOne(queryWrapper);
        if (video == null) {
            throw new BusinessException(StatusCodeEnum.NOT_FOUND);
        }
        Map<Integer, VideoCategory> categoryMapInfo = categoryService.getCategoryMapInfo(Lists.list(video.getCategoryId()));
        return buildAdminVideoVo(video, categoryMapInfo);
    }

    @Override
    public void publishVideo(VideoDto videoDto) {
        //生成唯一随机视频编码
        if (videoDto.getIsMultiple() == BaseConstants.YES) {
            if (videoDto.getSeq() == null) {
                throw new BusinessException(StatusCodeEnum.USER_HANDLE.getCode(), "序列号不能为空");
            }
            //校验序列号是否重复
            LambdaQueryWrapper<Video> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Video::getRootId, BaseConstants.PARENT_MENU_ID).eq(Video::getSeq, videoDto.getSeq())
                    .eq(Video::getIsDelete, BaseConstants.UN_DELETED);
            Video one = getOne(queryWrapper);
            if (one != null) {
                throw new BusinessException(StatusCodeEnum.USER_HANDLE.getCode(), "序列号重复");
            }
        }
        String videoCode = RandomUtil.randomStringUpper(10);
        Video video = videoDto.toPo();
        String tags = String.join(BusinessConstants.SPLIT, videoDto.getTagList());
        video.setVideoCode(videoCode);
        video.setTag(tags);
        save(video);
    }

    @Override
    public void updateVideo(VideoDto videoDto) {
        LambdaUpdateWrapper<Video> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Video::getVideoCode, videoDto.getVideoCode())
                .set(Video::getVideoName, videoDto.getVideoName())
                .set(Video::getActor, videoDto.getActor())
                .set(Video::getDirector, videoDto.getDirector())
                .set(Video::getVideoDesc, videoDto.getVideoDesc())
                .set(Video::getSeq, videoDto.getSeq())
                .set(Video::getArea, videoDto.getArea())
                .set(Video::getLanguage, videoDto.getLanguage())
                .set(Video::getTag,String.join(BusinessConstants.SPLIT,videoDto.getTagList()))
                .set(Video::getCategoryId, videoDto.getCategoryId());
        update(updateWrapper);

    }

    @Override
    public void mergeVideoFile(MergeFileDto mergeFileDto) {
        try {
            uploadFileService.mergeFile(mergeFileDto);
        } catch (ExecutionException | InterruptedException | FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public PageUtils<VideoListItemVo> getVideoList(PageQueryVideoDto queryVideoDto) {
        LambdaQueryWrapper<Video> wrapper = queryVideoDto.getWrapper();
        if (queryVideoDto.isDeepCategory()) {
            //获取该父级分类下的子分类
            List<VideoCategory> childrenCategoryList = categoryService.getChildrenCategoryList(queryVideoDto.getCategoryId());
            if (!childrenCategoryList.isEmpty()) {
                wrapper.or()
                        .in(Video::getCategoryId, childrenCategoryList.stream().map(VideoCategory::getCategoryId).collect(Collectors.toList()))
                        .eq(Video::getIsDelete, BaseConstants.UN_DELETED);
            }
        }
        Page<Video> page = queryVideoDto.getPage();
        page = page(page, wrapper);
        List<Video> records = page.getRecords();
        if (records.isEmpty()) {
            return new PageUtils<>(Collections.emptyList(), page);
        }
        List<VideoListItemVo> result = records.stream().map(video -> {
            VideoListItemVo videoListItemVo = new VideoListItemVo();
            BeanUtils.copyProperties(video, videoListItemVo);
            if (StringUtils.isNotBlank(video.getActor())) {
                videoListItemVo.setActorList(Arrays.asList(video.getActor().split(BusinessConstants.SPLIT)));
            }
            return videoListItemVo;
        }).collect(Collectors.toList());
        return new PageUtils<>(result, page);
    }

    @Override
    public List<VideoListItemVo> getRangeVideoList(Integer size) {
        return videoMapper.queryRangeVideo(size);
    }

    @Override
    public List<VideoListItemVo> getVideoItemListByVideoCode(List<String> videoCodeList) {
        LambdaQueryWrapper<Video> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Video::getIsDelete, BaseConstants.UN_DELETED)
                .in(Video::getVideoCode, videoCodeList);
        return list(wrapper).stream().map(video -> {
            VideoListItemVo videoListItemVo = new VideoListItemVo();
            BeanUtils.copyProperties(video, videoListItemVo);
            if (StringUtils.isNotBlank(video.getActor())) {
                videoListItemVo.setActorList(Arrays.asList(video.getActor().split(BusinessConstants.SPLIT)));
            }
            return videoListItemVo;
        }).collect(Collectors.toList());

    }

    @Override
    public List<SeqVideoItem> getVideoSeqList(Integer videoId) {
        return videoMapper.getVideoSeqList(videoId);
    }

    @Override
    public Boolean checkVideoSourceIsExist(CheckFileExistDto dto) {
        Boolean flag;
        LambdaQueryWrapper<UploadFileRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UploadFileRecord::getMd5,dto.getMd5()).eq(UploadFileRecord::getType,BusinessConstants.FILE_TYPE_WHOLE)
                .eq(UploadFileRecord::getIsDelete,BaseConstants.UN_DELETED);
        UploadFileRecord one = uploadFileService.getOne(queryWrapper);
        flag=one==null;
        if (flag){
            videoMapper.updateVideoSource(dto.getVideoCode(),one.getUrl());
        }
        return flag;
    }
}
